package com.wtpaint.lib.zhttp;

import android.support.annotation.NonNull;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *  调度器 通过一个线程池来维护线程  ：等待队列和执行队列存放不同状态的线程
 */
public class Dispatcher {

    private int maxRequests = 64;//最大请求连接数
    private int maxRequestsPreHost = 5;//单域名最大并发请求数量

    private Deque<Call.AsyncCall> readyAsyncCalls = new ArrayDeque<>();//等待执行的队列
    private Deque<Call.AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在执行的队列

    private ExecutorService executorService;//线程池

    public synchronized ExecutorService executorService() {
        if (null == executorService) {
            //通过线程工厂创建线程
            ThreadFactory threadFactory = new ThreadFactory() {
                @Override
                public Thread newThread(@NonNull Runnable r) {
                    return new Thread(r, "z http client");
                }
            };
            executorService = new ThreadPoolExecutor(
                    0,
                    Integer.MAX_VALUE,
                    60,
                    TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    threadFactory);
        }
        return executorService;
    }

    public Dispatcher(int maxRequests, int maxRequestsPreHost) {
        this.maxRequests = maxRequests;
        this.maxRequestsPreHost = maxRequestsPreHost;
    }

    public Dispatcher() {
        this(64, 5);
    }

    public void enqueue(Call.AsyncCall call) {
        if (runningAsyncCalls.size() > maxRequests &&
                runningCallsForHostSize(call) < maxRequestsPreHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }

    private int runningCallsForHostSize(Call.AsyncCall call) {
        int result = 0;
        for (Call.AsyncCall c : runningAsyncCalls) {
            if (c.host().equals(call.host())) {
                result++;
            }
        }
        return result;
    }

    public void finished(Call.AsyncCall call) {
        synchronized (this) {
            runningAsyncCalls.remove(call);
            checkReady();
        }
    }

    private void checkReady() {
        //
        if (runningAsyncCalls.size() >= maxRequests) {
            return;
        }

        if (readyAsyncCalls.isEmpty()) {
            return;
        }

        Iterator<Call.AsyncCall> iterator = readyAsyncCalls.iterator();
        while (iterator.hasNext()) {
            Call.AsyncCall call = iterator.next();
            if (runningCallsForHostSize(call) < maxRequestsPreHost) {
                iterator.remove();
                readyAsyncCalls.add(call);
                executorService().execute(call);
            }

            if (runningAsyncCalls.size() >= maxRequests) {
                return;
            }
        }

    }

}
